#!/usr/bin/env python
# -*- coding: utf-8 -*-

import asyncio
import tempfile
import uuid
from datetime import datetime
from pathlib import Path
from typing import List, Optional, Dict, Any, Tuple
from dataclasses import dataclass
import logging
import shutil

# 导入转换器管理器
from .converters.converter_manager import converter_manager
from .converters.base_converter import FileInfo
from .converters.work_dir_config import work_dir_config

# 导入OBS上传器
try:
    from obs_uploader import ObsUploader
    HAS_OBS_UPLOADER = True
except ImportError:
    HAS_OBS_UPLOADER = False
    logging.warning("obs_uploader模块未找到，OBS上传功能将不可用")

logger = logging.getLogger(__name__)

@dataclass
class ObsFileInfo:
    """OBS文件信息"""
    file_id: str
    file_size: int
    file_url: str

@dataclass
class ConversionResult:
    """转换结果"""
    file_id: str
    markdown_content: str
    files: List[Dict[str, Any]]  # 改为通用的文件列表，包含OBS文件信息
    success: bool
    error_message: Optional[str] = None
    
    # 保持向后兼容
    @property
    def obs_file_list(self) -> List[ObsFileInfo]:
        """向后兼容的OBS文件列表"""
        obs_list = []
        for file_dict in self.files:
            if file_dict.get('type') == 'obs':
                obs_list.append(ObsFileInfo(
                    file_id=file_dict['id'],
                    file_size=file_dict['size'],
                    file_url=file_dict['url']
                ))
        return obs_list

class DocumentConverter:
    """文档转换器主类"""
    
    def __init__(self, obs_config: Optional[Dict[str, Any]] = None, work_temp_dir: Optional[str] = None):
        """
        初始化文档转换器
        
        Args:
            obs_config: OBS配置，包含访问密钥等信息。如果为None，会尝试从环境变量读取
            work_temp_dir: 工作临时目录，如果为None，会从环境变量WORK_TEMP_DIR读取
        """
        self.obs_config = obs_config
        self.obs_uploader = None
        
        # 初始化工作目录配置
        work_dir_config.initialize(work_temp_dir)
        
        # 初始化OBS上传器
        if HAS_OBS_UPLOADER:
            try:
                # 传入配置或让OBS上传器自己从环境变量读取
                self.obs_uploader = ObsUploader(obs_config)
                if self.obs_uploader.is_available():
                    logger.info("OBS上传器初始化成功")
                else:
                    logger.info("OBS上传器已创建，但服务不可用（配置不完整或SDK未安装）")
            except Exception as e:
                logger.warning(f"OBS上传器初始化失败: {e}")
    
    def generate_file_id(self) -> str:
        """生成唯一文件ID"""
        now = datetime.now()
        date_part = now.strftime("%Y%m%d_%H%M%S")
        uuid_part = str(uuid.uuid4())[:8]
        return f"{date_part}_{uuid_part}"
    
    def check_file_support(self, file_path: Path) -> Tuple[bool, str]:
        """
        检查文件是否支持转换
        
        Args:
            file_path: 文件路径
            
        Returns:
            tuple: (是否支持, 错误信息)
        """
        if not file_path.exists():
            return False, f"文件不存在: {file_path}"
        
        if not file_path.is_file():
            return False, f"路径不是文件: {file_path}"
        
        converter_type = converter_manager.get_converter_type(file_path)
        if not converter_type:
            supported_formats = list(converter_manager._ext_to_converter.keys())
            return False, f"不支持的文件格式: {file_path.suffix}。支持的格式: {', '.join(supported_formats)}"
        
        return True, ""
    
    def get_available_converters_info(self) -> Dict[str, Dict[str, Any]]:
        """获取可用转换器信息"""
        info = {}
        
        # 遍历所有支持的文件类型
        for file_ext, converter_type in converter_manager._ext_to_converter.items():
            try:
                # 获取转换器实例（不会重复创建）
                converter = converter_manager.get_converter(converter_type)
                
                available_methods = converter.get_available_methods()
                missing_deps = []
                
                # 检查缺失的依赖
                if hasattr(converter, 'OPTIONAL_DEPENDENCIES'):
                    for dep_name, package in converter.OPTIONAL_DEPENDENCIES.items():
                        if not converter.is_dependency_available(dep_name):
                            missing_deps.append(f"{dep_name} (pip install {package})")
                
                # 检查系统级依赖（如LibreOffice）
                if hasattr(converter, 'SYSTEM_DEPENDENCIES'):
                    for dep_name, dep_info in converter.SYSTEM_DEPENDENCIES.items():
                        if hasattr(converter, 'system_deps_available') and not converter.system_deps_available.get(dep_name, False):
                            if dep_name == 'libreoffice':
                                install_info = converter.get_libreoffice_install_info()
                                missing_deps.append(f"{dep_name} (系统级安装: {install_info})")
                            else:
                                missing_deps.append(f"{dep_name} (系统级依赖)")
                
                # 特殊处理DOCX转换器的状态
                if file_ext == '.docx':
                    # 只保留 docling 和 pandoc
                    available_methods = [m for m in available_methods if m in ['docling', 'pandoc']]
                
                status = 'available' if available_methods else 'no_dependencies'
                if converter.__class__.__name__ == 'DOCXConverter':
                    # DOCX转换器的特殊状态信息
                    if hasattr(converter, 'get_status_info'):
                        status_info = converter.get_status_info()
                        if status_info['docx_support'] and status_info['doc_support']:
                            status = 'available'
                        elif status_info['docx_support']:
                            status = 'partial'  # 只支持DOCX，不支持DOC
                        else:
                            status = 'no_dependencies'
                
                info[file_ext] = {
                    'converter': converter.__class__.__name__,
                    'available_methods': available_methods,
                    'missing_dependencies': missing_deps,
                    'status': status
                }
                
                # 为DOCX转换器添加额外信息
                if converter.__class__.__name__ == 'DOCXConverter' and hasattr(converter, 'get_status_info'):
                    status_info = converter.get_status_info()
                    info[file_ext]['docx_support'] = status_info['docx_support']
                    info[file_ext]['doc_support'] = status_info['doc_support']
                    info[file_ext]['libreoffice_available'] = status_info['libreoffice_available']
                
            except Exception as e:
                info[file_ext] = {
                    'converter': f"{converter_type}Converter",
                    'available_methods': [],
                    'missing_dependencies': ['所有依赖'],
                    'status': 'error',
                    'error': str(e)
                }
        
        return info
    
    def print_converter_status(self):
        """打印转换器状态信息"""
        info = self.get_available_converters_info()
        
        print("\n=== 文档转换器状态 ===")
        
        for file_ext, converter_info in info.items():
            print(f"\n{file_ext.upper()} 文件:")
            print(f"  转换器: {converter_info['converter']}")
            print(f"  状态: {converter_info['status']}")
            
            if converter_info['available_methods']:
                print(f"  可用方法: {', '.join(converter_info['available_methods'])}")
            
            if converter_info['missing_dependencies']:
                print(f"  缺失依赖: ")
                for dep in converter_info['missing_dependencies']:
                    print(f"    - {dep}")
            
            # 特殊显示DOCX转换器的详细信息
            if converter_info['converter'] == 'DOCXConverter':
                if 'docx_support' in converter_info:
                    print(f"  DOCX支持: {'✅' if converter_info['docx_support'] else '❌'}")
                if 'doc_support' in converter_info:
                    print(f"  DOC支持: {'✅' if converter_info['doc_support'] else '❌'} (需要LibreOffice)")
                if 'libreoffice_available' in converter_info:
                    print(f"  LibreOffice: {'✅ 已安装' if converter_info['libreoffice_available'] else '❌ 未安装'}")
            
            if 'error' in converter_info:
                print(f"  错误: {converter_info['error']}")
        
        # 显示OBS上传功能状态
        if self.obs_uploader:
            if self.obs_uploader.is_available():
                print(f"\nOBS上传功能: ✅ 可用")
                obs_status = self.obs_uploader.get_config_status()
                print(f"  存储桶: {obs_status.get('bucket', 'N/A')}")
                print(f"  终端点: {obs_status.get('endpoint', 'N/A')}")
                print(f"  凭据状态: {'✅ 已配置' if obs_status.get('has_credentials') else '❌ 未配置'}")
            else:
                print(f"\nOBS上传功能: ⚠️ 已初始化但不可用")
                obs_status = self.obs_uploader.get_config_status()
                print(f"  SDK可用: {'✅' if obs_status.get('sdk_available') else '❌'}")
                print(f"  客户端: {'✅' if obs_status.get('client_initialized') else '❌'}")
                print(f"  凭据: {'✅' if obs_status.get('has_credentials') else '❌'}")
        else:
            print(f"\nOBS上传功能: ❌ 不可用（模块导入失败）")
        
        # 显示转换器管理器统计信息
        stats = converter_manager.get_statistics()
        print(f"\n=== 转换器实例统计 ===")
        print(f"总实例数: {stats['total_instances']}")
        print(f"总使用次数: {stats['total_usage']}")
        
        if stats['instances']:
            print("实例详情:")
            for instance_key, instance_stats in stats['instances'].items():
                print(f"  {instance_key}: 使用{instance_stats['use_count']}次, 运行{instance_stats['uptime_hours']:.1f}小时")
        
        print("=" * 50)
    
    async def convert_file(
        self, 
        file_path: str, 
        conversion_method: Optional[str] = None,
        **kwargs
    ) -> ConversionResult:
        """
        转换单个文件
        
        Args:
            file_path: 文件路径
            conversion_method: 转换方法（可选）
            **kwargs: 其他转换参数
            
        Returns:
            ConversionResult: 转换结果
        """
        file_path_obj = Path(file_path)
        file_id = self.generate_file_id()
        
        logger.info(f"开始转换文件: {file_path_obj.name} (ID: {file_id})")
        
        # 检查文件支持
        supported, error_msg = self.check_file_support(file_path_obj)
        if not supported:
            logger.error(error_msg)
            return ConversionResult(
                file_id=file_id,
                markdown_content="",
                files=[],
                success=False,
                error_message=error_msg
            )
        
        try:
            # 获取转换器类型
            converter_type = converter_manager.get_converter_type(file_path_obj)
            if not converter_type:
                error_msg = f"无法确定文件类型: {file_path_obj.suffix}"
                return ConversionResult(
                    file_id=file_id,
                    markdown_content="",
                    files=[],
                    success=False,
                    error_message=error_msg
                )
            
            # 获取转换器实例（单例模式，会重用已创建的实例）
            converter = converter_manager.get_converter(converter_type, conversion_method, file_id)
            
            # 检查转换器是否有可用方法
            available_methods = converter.get_available_methods()
            if not available_methods:
                missing_info = []
                if hasattr(converter, 'OPTIONAL_DEPENDENCIES'):
                    for dep_name, package in converter.OPTIONAL_DEPENDENCIES.items():
                        missing_info.append(f"pip install {package}")
                
                error_msg = (
                    f"无法转换 {file_path_obj.suffix} 文件，缺少必要的依赖。\n"
                    f"请安装以下任一依赖包：\n" + 
                    "\n".join(f"- {info}" for info in missing_info)
                )
                logger.error(error_msg)
                return ConversionResult(
                    file_id=file_id,
                    markdown_content="",
                    files=[],
                    success=False,
                    error_message=error_msg
                )
            
            # 执行转换（返回markdown内容和FileInfo列表）
            try:
                markdown_content, file_infos = await converter.convert(
                    file_path_obj, 
                    method=conversion_method,
                    **kwargs
                )
            except ImportError as e:
                # 如果指定的方法不可用，提供友好的错误信息
                if conversion_method:
                    error_msg = (
                        f"转换方法 '{conversion_method}' 不可用: {str(e)}\n"
                        f"可用的转换方法: {', '.join(available_methods)}"
                    )
                else:
                    error_msg = str(e)
                
                logger.error(error_msg)
                return ConversionResult(
                    file_id=file_id,
                    markdown_content="",
                    files=[],
                    success=False,
                    error_message=error_msg
                )
            
            # 上传图片文件到OBS
            obs_file_list = []
            if self.obs_uploader and file_infos:
                try:
                    obs_file_list = await self._upload_files_to_obs(
                        file_id, 
                        file_infos
                    )
                    
                    # 更新markdown中的图片链接
                    markdown_content = self._update_markdown_image_links(
                        markdown_content, 
                        file_infos, 
                        obs_file_list
                    )
                    
                except Exception as e:
                    logger.warning(f"OBS上传失败: {e}")
            
            # 构建返回的文件列表（只包含OBS上传的图片文件）
            files_result = []
            for obs_info in obs_file_list:
                files_result.append({
                    'type': 'obs',
                    'id': obs_info.file_id,  # 文件名
                    'filename': obs_info.file_id,  # 文件名
                    'size': obs_info.file_size,
                    'url': obs_info.file_url
                })
            
            logger.info(f"文件转换成功: {file_path_obj.name} (ID: {file_id})")
            logger.info(f"上传到OBS的图片文件: {len(files_result)} 个")
            
            return ConversionResult(
                file_id=file_id,
                markdown_content=markdown_content,
                files=files_result,
                success=True
            )
            
        except Exception as e:
            error_msg = f"转换失败: {str(e)}"
            logger.error(error_msg)
            return ConversionResult(
                file_id=file_id,
                markdown_content="",
                files=[],
                success=False,
                error_message=error_msg
            )
    
    async def convert_files(
        self, 
        file_paths: List[str],
        conversion_methods: Optional[Dict[str, str]] = None,
        **kwargs
    ) -> List[ConversionResult]:
        """
        批量转换文件
        
        Args:
            file_paths: 文件路径列表
            conversion_methods: 文件特定的转换方法映射 {文件路径: 转换方法}
            **kwargs: 其他转换参数
            
        Returns:
            List[ConversionResult]: 转换结果列表
        """
        conversion_methods = conversion_methods or {}
        
        logger.info(f"开始批量转换 {len(file_paths)} 个文件")
        
        # 并发执行转换
        tasks = []
        for file_path in file_paths:
            method = conversion_methods.get(file_path)
            task = self.convert_file(file_path, method, **kwargs)
            tasks.append(task)
        
        results = await asyncio.gather(*tasks, return_exceptions=True)
        
        # 处理异常结果
        processed_results = []
        for i, result in enumerate(results):
            if isinstance(result, Exception):
                file_id = self.generate_file_id()
                error_result = ConversionResult(
                    file_id=file_id,
                    markdown_content="",
                    files=[],
                    success=False,
                    error_message=f"转换异常: {str(result)}"
                )
                processed_results.append(error_result)
                logger.error(f"文件 {file_paths[i]} 转换异常: {result}")
            else:
                processed_results.append(result)
        
        successful_count = sum(1 for r in processed_results if r.success)
        logger.info(f"批量转换完成: {successful_count}/{len(file_paths)} 个文件成功")
        
        return processed_results
    
    # 转换器管理方法
    def restart_converter(self, converter_type: str, method: Optional[str] = None):
        """重启指定的转换器实例"""
        return converter_manager.restart_instance(converter_type, method)
    
    def restart_all_converters(self):
        """重启所有转换器实例"""
        return converter_manager.restart_all_instances()
    
    def get_converter_statistics(self):
        """获取转换器使用统计"""
        return converter_manager.get_statistics()
    
    async def _upload_files_to_obs(
        self, 
        file_id: str, 
        file_infos: List[FileInfo]
    ) -> List[ObsFileInfo]:
        """只上传图片文件到OBS"""
        if not self.obs_uploader:
            return []
        
        obs_file_list = []
        
        # 只上传图片文件，不上传原始文件和MD文件
        image_files = [f for f in file_infos if f.file_type == "image"]
        
        if not image_files:
            logger.info("没有图片文件需要上传到OBS")
            return obs_file_list
        
        logger.info(f"准备上传 {len(image_files)} 个图片文件到OBS")
        
        for file_info in image_files:
            try:
                # 使用md前缀和文件ID作为OBS目录前缀
                obs_key = f"md/{file_id}/{file_info.file_name}"
                
                # 检查文件是否存在
                file_path = Path(file_info.file_path)
                if not file_path.exists():
                    logger.warning(f"图片文件不存在，跳过上传: {file_info.file_path}")
                    continue
                
                # 上传文件
                result = await self.obs_uploader.upload_file(
                    file_info.file_path, 
                    obs_key
                )
                
                if result.get('success'):
                    obs_info = ObsFileInfo(
                        file_id=file_info.file_name,  # 使用文件名作为识别
                        file_size=file_info.file_size,
                        file_url=result['file_url']
                    )
                    obs_file_list.append(obs_info)
                    logger.info(f"图片已上传到OBS: {file_info.file_name} -> {result['file_url']}")
                else:
                    logger.warning(f"图片上传失败: {file_info.file_name}")
                    
            except Exception as e:
                logger.warning(f"上传图片 {file_info.file_name} 到OBS失败: {e}")
        
        logger.info(f"成功上传 {len(obs_file_list)} 个图片文件到OBS")
        return obs_file_list
    
    def _update_markdown_image_links(
        self, 
        markdown_content: str, 
        file_infos: List[FileInfo], 
        obs_file_list: List[ObsFileInfo]
    ) -> str:
        """更新markdown中的图片链接为OBS链接"""
        if not obs_file_list:
            logger.info("没有OBS文件链接，不需要更新Markdown图片链接")
            return markdown_content
        
        logger.info(f"开始更新Markdown中的 {len(obs_file_list)} 个图片链接")
        
        # 创建文件名到OBS URL的映射
        filename_to_url = {}
        for obs_info in obs_file_list:
            # obs_info.file_id 现在存储的是文件名
            filename_to_url[obs_info.file_id] = obs_info.file_url
        
        # 使用正则表达式更准确地替换图片链接
        import re
        import urllib.parse
        
        updated_content = markdown_content
        replacement_count = 0
        
        for filename, obs_url in filename_to_url.items():
            # 生成文件名的不同编码形式
            encoded_filename = urllib.parse.quote(filename)
            
            # 匹配各种Markdown图片语法（包括URL编码的文件名）
            patterns = [
                # ![alt text](filename) - 原始文件名
                (re.compile(rf'!\[([^\]]*)\]\(([^)]*{re.escape(filename)}[^)]*)\)', re.IGNORECASE), 
                 rf'![\1]({obs_url})'),
                # ![alt text](encoded_filename) - URL编码的文件名
                (re.compile(rf'!\[([^\]]*)\]\(([^)]*{re.escape(encoded_filename)}[^)]*)\)', re.IGNORECASE), 
                 rf'![\1]({obs_url})'),
                # 直接的文件路径引用 - 只替换在图片标签中的
                (re.compile(rf'!\[[^\]]*\]\([^)]*([^/)]+/{re.escape(filename)})\)', re.IGNORECASE),
                 lambda m: m.group(0).replace(m.group(1), obs_url)),
                # URL编码的路径引用
                (re.compile(rf'!\[[^\]]*\]\([^)]*([^/)]+/{re.escape(encoded_filename)})\)', re.IGNORECASE),
                 lambda m: m.group(0).replace(m.group(1), obs_url)),
            ]
            
            for pattern, replacement in patterns:
                if callable(replacement):
                    # 使用函数替换
                    new_content, count = pattern.subn(replacement, updated_content)
                else:
                    # 使用字符串替换
                    new_content, count = pattern.subn(replacement, updated_content)
                
                if count > 0:
                    replacement_count += count
                    updated_content = new_content
                    logger.debug(f"替换了 {count} 个 {filename} 的引用")
        
        # 额外处理相对路径的情况
        for filename, obs_url in filename_to_url.items():
            encoded_filename = urllib.parse.quote(filename)
            
            # 处理类似 ./artifacts/image.png 或 artifacts/image.png 的路径（包括URL编码）
            relative_patterns = [
                rf'!\[([^\]]*)\]\(([^)]*[./]*[^/]*/{re.escape(filename)})\)',
                rf'!\[([^\]]*)\]\(([^)]*{re.escape(filename)})\)',
                rf'!\[([^\]]*)\]\(([^)]*[./]*[^/]*/{re.escape(encoded_filename)})\)',
                rf'!\[([^\]]*)\]\(([^)]*{re.escape(encoded_filename)})\)',
            ]
            
            for pattern in relative_patterns:
                regex = re.compile(pattern, re.IGNORECASE)
                matches = regex.findall(updated_content)
                if matches:
                    new_content = regex.sub(rf'![\1]({obs_url})', updated_content)
                    if new_content != updated_content:
                        count = len(matches)
                        replacement_count += count
                        updated_content = new_content
                        logger.debug(f"替换了 {count} 个相对路径 {filename}/{encoded_filename} 的引用")
        
        logger.info(f"完成Markdown图片链接更新，共替换 {replacement_count} 个链接")
        
        # 如果有替换，记录一些示例用于调试
        if replacement_count > 0:
            # 显示前几个OBS链接作为示例
            example_urls = list(filename_to_url.values())[:3]
            logger.debug(f"示例OBS链接: {example_urls}")
        
        return updated_content 